package tools

import (
	"context"
	"math/rand"
	"strings"
	"time"

	commonConstant "algo-sdk-go/common/constant"

	"github.com/globalsign/mgo/bson"
)

var (
	StringCast     = func(str string) string { return str }
	TrimSpace      = func(t string) string { return strings.TrimSpace(t) }
	Int32Cast      = func(int32Value int32) int32 { return int32Value }
	IntCast        = func(intValue int) int { return intValue }
	StringNotBlank = func(str string) bool { return str == "" }
	StringNotEmpty = func(str string) bool { return str != "" }
)

func TernaryExpression[T any](expression bool, trueValue, falseValue T) T {
	return map[bool]T{true: trueValue, false: falseValue}[expression]
}

func Unique[K, T comparable](array []T, unique func(T) K) []T {
	var (
		mK          = map[K]struct{}{}
		uniqueArray = make([]T, 0)
	)

	for _, item := range array {
		key := unique(item)
		if _, exist := mK[key]; exist {
			continue
		}
		uniqueArray = append(uniqueArray, item)
		mK[key] = struct{}{}
	}
	return uniqueArray
}
func Filter[T comparable](array []T, filter func(T) bool) []T {
	var filterArray = make([]T, 0)
	for _, item := range array {
		if filter != nil && filter(item) {
			continue
		}
		filterArray = append(filterArray, item)
	}
	return filterArray
}
func Catch[T comparable](array []T, filter func(T) bool) []T {
	var catchArray = make([]T, 0)
	for _, item := range array {
		if filter != nil && filter(item) {
			catchArray = append(catchArray, item)
		}
	}
	return catchArray
}
func Cast[K, T any](array []T, cast func(T) K) []K {
	var castArray = make([]K, 0)
	for _, item := range array {
		castArray = append(castArray, cast(item))
	}
	return castArray
}
func UniqueAndFilter[K comparable, T any](array []T, unique func(T) K, filter func(T) bool) []T {
	var (
		m           = map[K]struct{}{}
		uniqueArray = make([]T, 0, len(m))
	)

	for _, item := range array {
		key := unique(item)
		if _, exist := m[key]; exist {
			continue
		}
		if filter != nil && filter(item) {
			continue
		}
		uniqueArray = append(uniqueArray, item)
		m[key] = struct{}{}
	}
	return uniqueArray
}
func UniqueAndFilterAndCast[K comparable, T, F any](array []T, unique func(T) K, filter func(T) bool, cast func(T) F) []F {
	var (
		m           = map[K]struct{}{}
		uniqueArray = make([]F, 0, len(m))
	)

	for _, item := range array {
		key := unique(item)
		if _, exist := m[key]; exist {
			continue
		}
		if filter != nil && filter(item) {
			continue
		}
		uniqueArray = append(uniqueArray, cast(item))
		m[key] = struct{}{}
	}

	return uniqueArray
}
func FilterAndCast[T, F any](array []T, filter func(T) bool, cast func(T) F) []F {
	var (
		uniqueArray = make([]F, 0, len(array))
	)

	for _, item := range array {
		if filter != nil && filter(item) {
			continue
		}
		uniqueArray = append(uniqueArray, cast(item))
	}

	return uniqueArray
}

func FirstNotEmpty[T any](array []T, isNotEmpty func(T) bool) (T, bool) {
	var zeroValue T
	for _, value := range array {
		if isNotEmpty(value) {
			return value, true
		}
	}
	return zeroValue, false
}

func SliceSegmentation[T any](array []T, size int) [][]T {
	if len(array) == 0 || size <= 0 {
		return nil
	}
	matrixSize := (len(array) + size - 1) / size // 计算所需切片的数量
	result := make([][]T, 0, matrixSize)

	for i := 0; i < len(array); i += size {
		end := i + size
		if end > len(array) {
			end = len(array) // 确保不越界
		}
		result = append(result, array[i:end]) // 使用切片直接添加到结果中
	}

	return result
}

//  -------------    trace

func SetTraceId(ctx context.Context) context.Context {
	if ctx == nil {
		ctx = context.Background()
	}

	if value := ctx.Value(commonConstant.TraceId); value != nil && value.(string) != "" {
		return ctx
	}

	return SetContext(ctx, map[string]string{commonConstant.TraceId: bson.NewObjectId().Hex()})
}

func SetTrace(ctx context.Context, traceId string) context.Context {
	if ctx == nil {
		ctx = context.Background()
	}

	if value := ctx.Value(commonConstant.TraceId); value != nil && value.(string) != "" {
		return ctx
	}

	traceId = TernaryExpression(traceId != "", traceId, bson.NewObjectId().Hex())

	return SetContext(ctx, map[string]string{commonConstant.TraceId: traceId})
}

func GetTraceId(ctx context.Context) string {
	if ctx == nil {
		ctx = context.Background()
	}
	if value := ctx.Value(commonConstant.TraceId); value != nil {
		return value.(string)
	}
	return ""
}

func SetContext(ctx context.Context, value map[string]string) context.Context {
	if ctx == nil {
		ctx = context.Background()
	}

	for k, v := range value {
		ctx = context.WithValue(ctx, k, v)
	}
	return ctx
}

func GetSW8TraceId(str, symbol string) string {
	if symbol == "" {
		return ""
	}

	parts := strings.Split(str, symbol)
	if len(parts) >= 3 {
		midPart := parts[1]
		return midPart
	}
	return ""
}

func ReduceRand() int {
	rand.Seed(time.Now().UnixNano())
	randomNumber := rand.Intn(900000) + 100000
	return randomNumber
}
